import asyncio
import os
import json
import sys
from typing import Optional
from contextlib import AsyncExitStack
from openai import OpenAI
from dotenv import load_dotenv
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

load_dotenv()

class MCPClient:
    def __init__(self):
        print("初始化MCPClient")
        self.exit_stack = AsyncExitStack()
        self.openai_api_key = os.getenv("DEEPSEEK_API_KEY")
        self.base_url = os.getenv("BASE_URL")
        self.model = os.getenv("MODEL_NAME")

        if not self.openai_api_key:
            raise ValueError("⚠ 未找到密钥，请确认.env文件中设置的DEEPSEEK_API_KEY")
        
        self.client = OpenAI(api_key=self.openai_api_key, base_url=self.base_url)
        self.session: Optional[ClientSession] = None

    async def connect_to_server(self, server_script_path: str): 
        is_python = server_script_path.endswith('.py')
        is_js = server_script_path.endswith('.js')
        if not (is_python or is_js):
            raise ValueError("⚠ 服务器脚本必须是 .py 或 .js 文件")
    
        try:
            #根据脚本文件执行服务脚本
            command = "python" if is_python else "node"
            server_params = StdioServerParameters(
                command=command,
                args=[server_script_path],
                env=None
            )
        except Exception as e:
            print(f"mcp服务脚本异常：{str(e)}")
        
        try:
            stdio_transport = await self.exit_stack.enter_async_context(
                stdio_client(server_params)
            )
            self.stdio, self.write = stdio_transport

            self.session = await self.exit_stack.enter_async_context(
                ClientSession(self.stdio, self.write)
            )
            await self.session.initialize()
        except Exception as e:
            print(f"启动mcp服务失败：{str(e)}")
        
        response = await self.session.list_tools()
        tools = response.tools
        
        print("\n已连接到服务器，支持工具:", [tool.name for tool in tools])

        

    async def process_query(self, query: str) -> str:
        messages = [{"role": "user", "content": query}]

        response = await self.session.list_tools()
        available_tools = [{
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "input_schema": tool.inputSchema
            }
        } for tool in response.tools]

        response = self.client.chat.completions.create(
            model=self.model,
            messages=messages,
            tools=available_tools
        )

        content = response.choices[0]
        if content.finish_reason == "tool_calls":
            tool_call = content.message.tool_calls[0]
            tool_name = tool_call.function.name
            tool_args = json.loads(tool_call.function.arguments)

            result = await self.session.call_tool(tool_name, tool_args)
            print(f"\n[调用工具 {tool_name} 参数 {tool_args}]\n")

            messages.append(content.message.model_dump())
            messages.append({
                "role": "tool",
                "content": result.content[0].text,
                "tool_call_id": tool_call.id
            })

            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages
            )

            return response.choices[0].message.content
        
        return content.message.content

    async def chat_loop(self):
        print("\n🤖 MCP客户端已启动！输入'quit'退出")
        while True:
            try:
                query = input("\n你：").strip()
                if query.lower() == 'quit':
                    break
                response = await self.process_query(query)
                print(f"\n🤖DeepSeek:{response}")
            except Exception as e:
                print(f"⚠ 发生错误：{str(e)}")

    async def cleanup(self):
        await self.exit_stack.aclose()

async def main():
    # 设置Windows兼容的事件循环策略
    if sys.platform == 'win32':
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

    if len(sys.argv) < 2:
        print("用法: python MCPClient.py <服务器脚本路径>")
        sys.exit(1)
    
    client = MCPClient()
    try:
        await client.connect_to_server(sys.argv[1])
        await client.chat_loop()
    finally:
        await client.cleanup()

if __name__ == "__main__":
    asyncio.run(main())
